﻿using Microsoft.ClearScript.V8;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.Application.Services;
using System.Linq;
using Microsoft.ClearScript;
using Microsoft.Extensions.Logging;

namespace EcoSystem
{
    public class ScriptAppService : IScriptAppService, IDisposable
    {
        private readonly V8ScriptEngine engine;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="core"></param>
        /// <param name="api"></param>
        /// <param name="store"></param>
        /// <param name="context"></param>
        public ScriptAppService(
            ICoreScriptService core,
            IApiScriptService api,
            IDataStoreScriptService store,
            IContextScriptService context,
            ILoggerAppService logger
            )
        {
            engine = new V8ScriptEngine();
            engine.AddHostObject("dotnet", new HostTypeCollection("mscorlib", "System.Core"));
            engine.AddHostType("Console", typeof(Console));
            engine.AddHostType("DateTimeOffset", typeof(DateTimeOffset));
            engine.AddHostType("DateTime", typeof(DateTime));
            engine.AddHostType("Dictionary", typeof(Dictionary<,>));
            engine.AddHostType("List", typeof(List<>));
            engine.AddHostObject("$core", core);
            engine.AddHostObject("$http", api);
            engine.AddHostObject("$store", store);
            engine.AddHostObject("$current", context);
            engine.AddHostObject("Logger", logger);
        }

        public void Dispose()
        {
            engine.Dispose();
        }

        public TResult ExecuteMethod<TResult>(string methodName, Dictionary<string, object> args)
        {
            var data = JsonConvert.SerializeObject(args);
            var result = engine.Evaluate($"{methodName}({data})");
            if (result is TResult)
            {
                return (TResult)result;
            }
            else
            {
                return default(TResult);
            }
        }

        public void ExecuteScript(string content)
        {
            engine.Execute(content);
        }

        public TResult ExecuteScript<TResult>(string content)
        {
            var result = engine.Evaluate(content);
            if (result is TResult)
            {
                return (TResult)result;
            }
            else
            {
                return default(TResult);
            }
        }

        public void RegisterMethod(string methodName, string content, string[] args)
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine($"function {methodName}(__args)");
            sb.AppendLine("{");
            foreach (string name in args)
            {
                sb.AppendLine($"let {name} = __args.{name};");
            }
            sb.AppendLine(content);
            sb.AppendLine("}");
            engine.Execute(sb.ToString());
        }
    }
}
